/*****************************************************************
* Avetis Petrosyan
* Rolando Douglas
* ECE 625
* Project Digital Scoreboard
* Real Time Interrupt Routines (note: RTI uses XTAL freq = 8MHz)
******************************************************************/


#include <hidef.h>      /* common defines and macros */
#include <stdio.h>
#include "derivative.h" /* derivative-specific definitions */
#include "rti.h"
#include "commands.h"


#define DisableInterrupt() __asm(SEI) 
#define EnableInterrupt() __asm(CLI)

#define GAME_MIN 45
#define CLOCK_ONCE_PER_SEC 217

#define PH0_TEAM_1_GOAL     0x01
#define PH2_DECREASE_TIME   0x02
#define PH1_TEAM_2_GOAL     0x04
#define PH3_END_OF_GAME     0x08

#define PH0_TEAM_1_PENALTY_KICK_GOAL      0x01
#define PH1_TEAM_1_PENALTY_KICK_NO_GOAL   0x02
#define PH2_TEAM_2_PENALTY_KICK_GOAL      0x04
#define PH3_TEAM_2_PENALTY_KICK_NO_GOAL   0x08

#define PENALTY_KICK_INDEX 13

#define OVERTIME 1

typedef struct
{
   unsigned char ucaTeam1[4];
   unsigned char ucaTeam2[4];
   unsigned char ucaTeam1Full[MAX_LCD_SIZE];
   unsigned char ucaTeam2Full[MAX_LCD_SIZE];   
   unsigned char ucTeam1Score;
   unsigned char ucTeam2Score;
   unsigned char ucTeam1PenaltyKickAttempts;
   unsigned char ucTeam2PenaltyKickAttempts;
   unsigned char ucMin;
   unsigned char ucPrevMin;
   unsigned char ucSec;
   unsigned char ucHalf;
   unsigned char ucDisableInterrupt;   
   unsigned char ucOvertime;
         
}MYSTRUCT_SCOREBOARD;

MYSTRUCT_SCOREBOARD g_sScoreboard;               

unsigned int COUNT;               

void InitializeScoreboard(void)
{     
   // Clear struct
   memset(&g_sScoreboard, 0, sizeof(g_sScoreboard));

      
   // Set default values
   strcpy(g_sScoreboard.ucaTeam1, "ARM");
   strcpy(g_sScoreboard.ucaTeam2, "CRA");   
   strcpy(g_sScoreboard.ucaTeam1Full, "Armenia");
   strcpy(g_sScoreboard.ucaTeam2Full, "Costa Rica");   
   g_sScoreboard.ucTeam1Score = 0;
   g_sScoreboard.ucTeam2Score = 0;
   g_sScoreboard.ucTeam1PenaltyKickAttempts = 0;
   g_sScoreboard.ucTeam2PenaltyKickAttempts = 0;
   g_sScoreboard.ucMin = GAME_MIN;
   g_sScoreboard.ucPrevMin = 0;
   g_sScoreboard.ucSec = 0;
   g_sScoreboard.ucHalf = 1;
   g_sScoreboard.ucDisableInterrupt = 0;         
   g_sScoreboard.ucOvertime = 0;
}

void DisplayStandardInterface(MYSTRUCT_SCOREBOARD *_sScoreboard)
{     
   unsigned char ucaHalf[] = "Half:";
   unsigned char ucColon = ':';
   
   ClearLCD();
   
   // Write team 1 information to board 
   WriteString(_sScoreboard->ucaTeam1, LINE_1, 0);   
   WriteNumber(_sScoreboard->ucTeam1Score, LINE_1, TEAM_SCORE_INDEX, NO_PADDING);      
   WriteChar(ucColon, LINE_1, 13);
   // Write time to board
   WriteTime(_sScoreboard->ucMin, &_sScoreboard->ucPrevMin, UPDATE_MIN, _sScoreboard->ucSec);
     
   
   // Write team 2 information to board   
   WriteString(_sScoreboard->ucaTeam2, LINE_2, 0);   
   WriteNumber(_sScoreboard->ucTeam2Score, LINE_2, TEAM_SCORE_INDEX, NO_PADDING);   
   WriteString(ucaHalf, LINE_2, 10);    
   // Write half to board
   WriteChar(_sScoreboard->ucHalf + '0', LINE_2, 15);
}

void DisplayPenaltyKickInterface(MYSTRUCT_SCOREBOARD *_sScoreboard)
{     
   unsigned char ucaStandardAttemptAmount[] = "/5";   
   unsigned char ucIndex = PENALTY_KICK_INDEX;
   ClearLCD();
   
   // Write team 1 information to board 
   WriteString(_sScoreboard->ucaTeam1, LINE_1, 0);   
   WriteNumber(_sScoreboard->ucTeam1Score, LINE_1, TEAM_SCORE_INDEX, NO_PADDING);      
   if(_sScoreboard->ucTeam1PenaltyKickAttempts > 9)
   {
      ucIndex = PENALTY_KICK_INDEX - 1;      
   }
   WriteNumber(_sScoreboard->ucTeam1PenaltyKickAttempts, LINE_1, ucIndex, NO_PADDING);   
   WriteString(ucaStandardAttemptAmount, LINE_1, 14);
   
   // Write team 2 information to board   
   WriteString(_sScoreboard->ucaTeam2, LINE_2, 0);   
   WriteNumber(_sScoreboard->ucTeam2Score, LINE_2, TEAM_SCORE_INDEX, NO_PADDING);   
   if(_sScoreboard->ucTeam2PenaltyKickAttempts > 9)
   {
      ucIndex = PENALTY_KICK_INDEX - 1;      
   }
   else
   {
      ucIndex = PENALTY_KICK_INDEX;
   }
   WriteNumber(_sScoreboard->ucTeam2PenaltyKickAttempts, LINE_2, ucIndex, NO_PADDING);
   WriteString(ucaStandardAttemptAmount, LINE_2, 14);
   
}

void PenaltyKicks()
{  
   // Go into overtime
   g_sScoreboard.ucOvertime = OVERTIME;   
   WriteString("Penalty Kicks", LINE_2, 2);      
   MSDelay(500);
   DisplayPenaltyKickInterface(&g_sScoreboard);   
}

void StartGame(void) 
{
   // Real time Interrupt (RTI) setup 
   // Enable RTI
   CRGINT = CRGINT | CRGINT_RTIE_MASK;   
   
   // Closest RTI setting to reach 1 second
   // 8MHz Crystal Oscillator
   // RTI Freq = 8MHz / (9*2^12) = 217.0139Hz
   // Period of time between interrupts = 1/217.0139Hz = 4.608ms      
   RTICTL = 0x38;
   COUNT  = 0;				
       
   // PORTH interrupt setup (Push Buttons below the DIP Switch)
   DDRH = 0xF0; // PORTTH as input (Bits 0to3)
   PIEH = 0x0F; // enable PTH interrupt
   PPSH = 0x00; // Make it Edge-Trig.   
   
   InitializeScoreboard();  
  
   DisplayStandardInterface(&g_sScoreboard);
  
   // Enable interrupts globally		
   EnableInterrupt();  		

   for(;;)    //  Stay here until game finishes
   {      
      // If game finished, break out of infinite for loop
      if(g_sScoreboard.ucDisableInterrupt == 1)              
      {           
         break;	 
      }
      
      MSDelay(100);                                                     

   } //stay here in this loop until an interrupt comes in.           

   // End the game
   EndGame();                                               
}

void EndGame(void)
{
   int nTie = 0;
   
   // Disable Timer Interrupt
   CRGINT = CRGINT & ~CRGINT_RTIE_MASK;
   
   nTie = DetermineWinner(g_sScoreboard.ucaTeam1Full, g_sScoreboard.ucaTeam2Full, g_sScoreboard.ucTeam1Score, g_sScoreboard.ucTeam2Score);

   if(nTie == TIE_GAME)      
   {
      PenaltyKicks();
   }
   else
   {
      // Disable PORTH interrupts
      PIEH = 0x00;          
      // Delay 1ms
      MSDelay(1);
      // Disable interrupts globally   
      DisableInterrupt();
      // Exit the application
      exit(0);
   }   
}

void IsItGame(void)
{
   
   if(// By the 3rd penalty kick, there can potentially be a winner (if one team is leading by 3)            
      ((g_sScoreboard.ucTeam1PenaltyKickAttempts >= 3 && g_sScoreboard.ucTeam2PenaltyKickAttempts >= 3) &&
      ((g_sScoreboard.ucTeam1Score - g_sScoreboard.ucTeam2Score) > 2 || (g_sScoreboard.ucTeam2Score - g_sScoreboard.ucTeam1Score) > 2))
      
      ||

      // By the 4th penalty kick, there can potentially be a winner (if one team is leading by 2)
      ((g_sScoreboard.ucTeam1PenaltyKickAttempts >= 4 && g_sScoreboard.ucTeam2PenaltyKickAttempts >= 4) &&
      ((g_sScoreboard.ucTeam1Score - g_sScoreboard.ucTeam2Score) > 1 || (g_sScoreboard.ucTeam2Score - g_sScoreboard.ucTeam1Score) > 1))
      
      ||
      
      // By 5 or more penalty kicks, there will be a winner (if one team is leading by 1)
      ((g_sScoreboard.ucTeam1PenaltyKickAttempts >= 5 && g_sScoreboard.ucTeam2PenaltyKickAttempts >= 5) && 
       (g_sScoreboard.ucTeam1PenaltyKickAttempts == g_sScoreboard.ucTeam2PenaltyKickAttempts) &&
      ((g_sScoreboard.ucTeam1Score - g_sScoreboard.ucTeam2Score) > 0 || (g_sScoreboard.ucTeam2Score - g_sScoreboard.ucTeam1Score) > 0)))
   {
      EndGame();
   }     
}

// Real Time Interrupt ISR (Used for Countdown Timer)
interrupt (((0x10000-Vrti)/2)-1) void RTI_ISR(void)
{     
   COUNT++;			   
   		
   // 217 * 4.608ms = 0.999936 s (approx 1 second) closest setting to 1 second
   if(COUNT == CLOCK_ONCE_PER_SEC)
   {        
      COUNT = 0;      			         
   
      if(g_sScoreboard.ucMin == 0 && g_sScoreboard.ucSec == 0)
      {
         // Game is over
         if(g_sScoreboard.ucHalf == 2)
         {  
            g_sScoreboard.ucDisableInterrupt = 1;
            return;
         }
         
         // Reset minute to GAME_MIN
         g_sScoreboard.ucMin = GAME_MIN;
         // Reset seconds to 0
         g_sScoreboard.ucSec = 0;
         // Change to 2nd Half
         g_sScoreboard.ucHalf = 2;
         // Write to board
         WriteChar(g_sScoreboard.ucHalf + '0', LINE_2, 15);
      }   

      if(g_sScoreboard.ucSec == 0 || g_sScoreboard.ucSec >= 60)
      {
         // Subtract a minute
         g_sScoreboard.ucMin--;
         // Reset seconds to 60
         g_sScoreboard.ucSec = 60;
      } 
    
      // Decrement seconds        
      g_sScoreboard.ucSec--;
      
      // Write the countdown time for the half    
      WriteTime(g_sScoreboard.ucMin, &g_sScoreboard.ucPrevMin, BASE_ON_PREV_MIN, g_sScoreboard.ucSec);   
   }
   
   // Clear it for next round	
   CRGFLG = CRGFLG | CRGFLG_RTIF_MASK; 
}

                          
// PORTH Interrupt (Switch Buttons)
interrupt (((0x10000-Vporth)/2)-1) void PORTH_ISR(void)
{
   if(g_sScoreboard.ucOvertime == OVERTIME)
   {            
      switch(PIFH)
      {
         case PH0_TEAM_1_PENALTY_KICK_GOAL:
            
            g_sScoreboard.ucTeam1PenaltyKickAttempts++;
            g_sScoreboard.ucTeam1Score++;	
            WriteGoal(20, g_sScoreboard.ucaTeam1Full);
            DisplayPenaltyKickInterface(&g_sScoreboard);          
            break;

         case PH1_TEAM_1_PENALTY_KICK_NO_GOAL:
   
            g_sScoreboard.ucTeam1PenaltyKickAttempts++;         
            WriteNoGoal(20, g_sScoreboard.ucaTeam1Full);
            DisplayPenaltyKickInterface(&g_sScoreboard);         
            break;
            
         case PH2_TEAM_2_PENALTY_KICK_GOAL:

            g_sScoreboard.ucTeam2PenaltyKickAttempts++;            
            g_sScoreboard.ucTeam2Score++;	
            WriteGoal(20, g_sScoreboard.ucaTeam2Full);
            DisplayPenaltyKickInterface(&g_sScoreboard);          
            break;

         case PH3_TEAM_2_PENALTY_KICK_NO_GOAL:
         
            g_sScoreboard.ucTeam2PenaltyKickAttempts++;
            WriteNoGoal(20, g_sScoreboard.ucaTeam2Full);            
            DisplayPenaltyKickInterface(&g_sScoreboard);         
            break;            
            
         default:
            
            break;
      }

      IsItGame();  
     

   }
   else
   {
      switch(PIFH)
      {
         case PH0_TEAM_1_GOAL:
            
            g_sScoreboard.ucTeam1Score++;	
            WriteGoal(20, g_sScoreboard.ucaTeam1Full);
            DisplayStandardInterface(&g_sScoreboard);         
            break;

         case PH2_DECREASE_TIME:
         
            DecreaseTime(&g_sScoreboard.ucMin);         
            MSDelay(250);         
            break;
            
         case PH1_TEAM_2_GOAL:
         
            g_sScoreboard.ucTeam2Score++;
            WriteGoal(20, g_sScoreboard.ucaTeam2Full);
            DisplayStandardInterface(&g_sScoreboard);         
            break;
            
         case PH3_END_OF_GAME:
                              
            EndGame();         
            break;
            
         default:
            break;
      }   
   }
   
   // Clear PTH Interupt Flags for the next round
   // Writing HIGH will clear the Interrupt flags   
   PIFH = PIFH | 0xFF;      
} 

